﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  NotificationTimeCondition.cs
//    
// Abstract:
//
//  This class represent the Time condition for Notification rules
//  
//--

using System;
using System.Diagnostics;
using System.Collections;

namespace Microsoft.EnhancedReminders
{
    /// <summary>
    /// Time range condition for notification rules.
    /// </summary>
    [Serializable()]
    public sealed class NotificationTimeCondition : NotificationCondition
    {
        private const int HoursPerDay = 24;
        private const int MinutesPerHalfHour = 30;

        /// <summary>
        /// Number of days in a week
        /// </summary>
        public const int NumberOfDays = 7;

        /// <summary>
        /// Number of 1/2 hour slots per day
        /// </summary>
        public const int IntervalsPerDay = 48;

        // Bitmap of time intervals where each bit represents a discrete time period within the week.
        private BitArray _timeIntervals = new BitArray(NumberOfDays * IntervalsPerDay);

        /// <summary>
        /// Constructor.
        /// </summary>
        public NotificationTimeCondition()
        { 
        }

        /// <summary>
        /// Copy constructor.
        /// </summary>
        /// <param name="timeCondition">The time condition to copy.</param>
        public NotificationTimeCondition(NotificationTimeCondition timeCondition)
            : base(timeCondition)
         {
            Debug.Assert(timeCondition != null, "timeCondition");
            Debug.Assert(timeCondition._timeIntervals != null, "timeCondition._timeIntervals");

            _timeIntervals = new BitArray(timeCondition._timeIntervals);
        }

        /// <summary>
        /// Determines if this time condition equals the specified condition.
        /// </summary>
        /// <param name="condition">Condition to compare against</param>
        /// <returns>
        /// true if the conditions are equivalent; false otherwise
        /// </returns>
        public override bool Equals(NotificationCondition condition)
        {
            Debug.Assert(condition != null, "condition != null");
            Debug.Assert(_timeIntervals != null, "_timeIntervals != null");

            if (!base.Equals(condition))
            {
                return false;
            }

            NotificationTimeCondition timeCondition = condition as NotificationTimeCondition;

            if (timeCondition == null)
            {
                return false;
            }

            Debug.Assert(timeCondition._timeIntervals != null, "timeCondition._timeIntervals != null");

            if (_timeIntervals.Length != timeCondition._timeIntervals.Length)
            {
                return false;
            }

            for (int i = 0; i < _timeIntervals.Length; i++)
            {
                if (_timeIntervals[i] != timeCondition._timeIntervals[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Determines if this condition matches the specified data.
        /// </summary>
        /// <param name="notificationData">Data</param>
        /// <returns>
        /// true if the condition matches the specified DateTime; false otherwise.
        /// </returns>
        public override bool IsSatisfied(INotificationData notificationData)
        {
            Debug.Assert(notificationData != null, "notificationData != null");
            Debug.Assert(_timeIntervals != null, "_timeIntervals != null");

            return _timeIntervals[IndexIntoArray(notificationData.DateTime)];
        }

        /// <summary>
        /// Time array represented as BitArray
        /// </summary>
        public BitArray TimeArray
        {
            get
            {
                return _timeIntervals;
            }
        }

        /// <summary>
        /// Calculate index into bit array from a converted DateTime
        /// </summary>
        /// <param name="convertedDateTime">DateTime object</param>
        /// <returns>Index into bit array</returns>
        private static int IndexIntoArray(DateTime convertedDateTime)
        {
            // Index of Day in a week
            int indexOfDay = (int)convertedDateTime.DayOfWeek;
            Debug.Assert(indexOfDay < NumberOfDays, "indexOfDay < NumberOfDays");

            // Index of day in bit array
            int indexOfDayInArray = indexOfDay * NotificationTimeCondition.IntervalsPerDay;

            Debug.Assert(indexOfDayInArray >= 0, "indexOfDayInArray >= 0");
            Debug.Assert(indexOfDayInArray < (NotificationTimeCondition.IntervalsPerDay * NotificationTimeCondition.NumberOfDays),
                "indexOfDayInArray < NotificationTimeCondition.IntervalsPerDay * NotificationTimeCondition.NumberOfDays");

            // Index of time within each day in bit array
            int indexOfTimeInArray = (convertedDateTime.TimeOfDay.Hours * NotificationTimeCondition.IntervalsPerDay / HoursPerDay);

            if (convertedDateTime.TimeOfDay.Minutes > MinutesPerHalfHour)
            {
                indexOfTimeInArray += 1;
            }

            Debug.Assert(indexOfTimeInArray <= NotificationTimeCondition.IntervalsPerDay,
                "indexOfTimeInArray < NotificationTimeCondition.IntervalsPerDay");

            return indexOfDayInArray + indexOfTimeInArray;
        }
    }
}
